<?php

/**
 * CCoreBaseDateTime
 *
 * @author Usenko Timur <usenko.timur@gmail.com>
 * @package base
 */

/**
 * @package base
 */
class CCoreBaseDateTime
{
	/**
	 * @var int
	 */
	private $_iUtcTimeStamp;

	/**
	 * @var int
	 */
	private $_iUtcTimeOffsetInSec;

	/**
	 * @param int $iTimeOffsetInSec = 0
	 * @return CCoreBaseDateTime
	 */
	public function __construct($iTimeOffsetInSec = 0)
	{
		$this->_iUtcTimeStamp = 0;
		$this->_iUtcTimeOffsetInSec = $iTimeOffsetInSec;
	}

	/**
	 * @param int $iTimeOffsetInSec
	 * @return CCoreBaseDateTime
	 */
	public function SetTimeOffset($iTimeOffsetInSec)
	{
		$this->_iUtcTimeOffsetInSec = $iTimeOffsetInSec;
		return $this;
	}

	/**
	 * Parse "Thu, 10 Jun 2010 08:58:33 -0700 (PDT)" (RFC 2822)
	 *
	 * @param string $sDateTime
	 * @return CCoreBaseDateTime
	 */
	public function ParseFromRFC2822DateString($sDateTime)
	{
		$sMatches = array();
		$sDatePattern = '/^(([a-z]*),[\s]*){0,1}(\d{1,2}).([a-z]*).(\d{2,4})[\s]*(\d{1,2}).(\d{1,2}).(\d{1,2})([\s]+([+-]?\d{1,4}))?([\s]*(\(?(\w+)\)?))?/i';
		if (preg_match($sDatePattern, trim($sDateTime), $sMatches) && count($sMatches) > 8)
		{
			$iMonth = $this->_getMonthIndexFromString($sMatches[4]);
			if ($iMonth == -1)
			{
				$iMonth = 1;
			}
			
			$iTimeStamp = gmmktime(
				$sMatches[6], $sMatches[7], $sMatches[8],
				$iMonth, $sMatches[3], $sMatches[5]);

			$sTimeOffset = $sTimeZone = '';
			if (isset($sMatches[10]))
			{
				$sTimeOffset = $sMatches[10];
			}
			if (isset($sMatches[13]))
			{
				$sTimeZone = trim($sMatches[13], ' ()');
			}

			$this->_iUtcTimeStamp = $this->_applyOffsetForDateTimeStamp($iTimeStamp, $sTimeOffset, $sTimeZone);
		}
		return $this;
	}

	/**
	 * Parse YYYY-MM-DD HH:II:SS
	 *
	 * @param string $sDateTime
	 * @return CCoreBaseDateTime
	 */
	public function ParseFromFromANSI($sDateTime)
	{
		$aMatches = array();
		$sDatePattern = '/^(\d{4})\-(\d{2})\-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/';
		if (preg_match($sDatePattern, trim($sDateTime), $aMatches) && count($aMatches) > 6)
		{
			$this->_iUtcTimeStamp = gmmktime(
				$aMatches[4], $aMatches[5], $aMatches[6],
				$aMatches[2], $aMatches[3], $aMatches[1]);
		}
		return $this;
	}

	/**
	 * Return timestamp in ANSI format
	 * 
	 * @return string
	 */
	public function ToANSI()
	{
		return $this->ToFormatString('Y-m-d H:i:s');
	}

	/**
	 * Return timestamp in RFC 2822 formatted date
	 *
	 * @return string
	 */
	public function ToRFC2822String()
	{
		$sResult = gmdate('D, j M Y H:i:s', $this->ToTimeStamp());
		$sResult .= ' '.$this->_getHoursStringFromTimeOffset($this->_iUtcTimeOffsetInSec);
		return $sResult;
	}

	/**
	 * @return string
	 */
	public function ToShortFormatString()
	{
		$iTimeStamp = $this->ToTimeStamp();
		$iNowTimeStamp = time();
		$sTime = gmdate('H:i', $iTimeStamp);

		if (gmdate('j', $iTimeStamp) + 0 == gmdate('j', $iNowTimeStamp) + 0 && gmdate('j n Y', $iTimeStamp) == gmdate('j n Y', $iNowTimeStamp))
		{
			return $sTime;
		}
		else if (gmdate('j', $iTimeStamp) + 1 == gmdate('j', $iNowTimeStamp) + 0 && gmdate('n Y', $iTimeStamp) == gmdate('n Y', $iNowTimeStamp))
		{
			return $sTime.' / Yesterday';
		}
		else if ($iTimeStamp > $iNowTimeStamp - 28512000)
		{
			return $sTime.' / '.gmdate('M', $iTimeStamp).' '.gmdate('j', $iTimeStamp);
		}

		return $sTime.' / '.gmdate('M', $iTimeStamp).' '.gmdate('j', $iTimeStamp).', '.gmdate('Y', $iTimeStamp);
	}

	/**
	 * @return string
	 */
	public function ToFormatString($sFormat)
	{
		if ('r' === $sFormat)
		{
			return $this->ToRFC2822String();
		}
		return gmdate($sFormat, $this->ToTimeStamp());
	}

	/**
	 * @return int
	 */
	public function ToTimeStamp()
	{
		return $this->_iUtcTimeStamp + $this->_iUtcTimeOffsetInSec;
	}

	/**
	 * @return int
	 */
	public function GetServerTimeOffset()
	{
		return (int) date('Z');
	}

	/**
	 * @return int
	 */
	public function GetUtcTimeStamp()
	{
		return (int) date('U');
	}

	/**
	 * @param string $sMonth
	 * @return int
	 */
	private function _getMonthIndexFromString($sMonth)
	{
		switch (strtolower($sMonth))
		{
			case 'jan':	return 1;
			case 'feb':	return 2;
			case 'mar':	return 3;
			case 'apr':	return 4;
			case 'may':	return 5;
			case 'jun':	return 6;
			case 'jul':	return 7;
			case 'aug':	return 8;
			case 'sep':	return 9;
			case 'oct':	return 10;
			case 'nov':	return 11;
			case 'dec':	return 12;
			default:	return -1;
		}
	}

	/**
	 * @param int $iTimeOffsetInSec
	 * @return string
	 */
	private function _getHoursStringFromTimeOffset($iTimeOffsetInSec)
	{
		$sResult = (0 <= $iTimeOffsetInSec) ? '+' : '-';
		$fDateTime = abs($iTimeOffsetInSec) / 3600;
		$iHour = (int) floor($fDateTime);
		$iMin = (int) (($fDateTime - $iHour) * 60);

		$sHour = str_pad((string) $iHour, 2, '0', STR_PAD_LEFT);
		$sMin = str_pad((string) $iMin, 2, '0', STR_PAD_LEFT);

		return $sResult.$sHour.$sMin;
	}

	/**
	 * @param string $sTimeOffset
	 * @return int
	 */
	private function _getTimeOffsetFromHoursString($sTimeOffset)
	{
		$iResult = 0;
		$sTimeOffset = trim($sTimeOffset);
		if (0 < strlen($sTimeOffset))
		{
			$sTimeOffset = substr($sTimeOffset, 1);
			$nOffset = (is_numeric($sTimeOffset)) ? (int) $sTimeOffset : 0;
			
			$iHours = $nOffset / 100;
			$iMinutes = $nOffset % 100;
			$iMultiplier = ('-' === $sTimeOffset{0}) ? -1 : 1;

			$iResult += $iMultiplier * $iHours * 60 * 60;
			$iResult += $iMultiplier * $iMinutes * 60;
		}
		
		return $iResult;
	}

	/**
	 * @param string $sTimeZone
	 * @return int
	 */
	private function _getTimeOffsetFromTimeZoneString($sTimeZone)
	{
		$iResult = 0;
		switch(strtolower($sTimeZone))
		{
			case 'ut':
			case 'gmt':
			case 'z':
				break;
			case 'est':
			case 'cdt':
				$iResult += 5 * 3600;
				break;
			case 'edt':
				$iResult += 4 * 3600;
				break;
			case 'cst':
			case 'mdt':
				$iResult += 6 * 3600;
				break;
			case 'mst':
			case 'pdt':
				$iResult += 7 * 3600;
				break;
			case 'pst':
				$iResult += 8 * 3600;
				break;
		}

		return $iResult;
	}
	
	/**
	 * @param int $iDateTimeStamp
	 * @param string $sTimeOffset
	 * @param string $sTimeZone
	 * @return int
	 */
	private function _applyOffsetForDateTimeStamp($iDateTimeStamp, $sTimeOffset, $sTimeZone)
	{
		$iResult = $iDateTimeStamp;
		if (0 < strlen($sTimeOffset))
		{
			$iResult += $this->_getTimeOffsetFromHoursString($sTimeOffset);
		}
		else if (null !== $sTimeZone && 0 < strlen($sTimeZone))
		{
			$iResult += $this->_getTimeOffsetFromTimeZoneString($sTimeZone);
		}

		return $iResult;
	}
}